/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#include "Game.hpp"

#include <cassert>
#include <OGRE/Ogre.h>
#include <QuickGUI.h>

#include "CameraMan.hpp"
#include "Client.hpp"
#include "FrameHandler.hpp"
#include "GameGUI.hpp"
#include "irrKlang.h"
#include "Local.hpp"
#include "MenuPresentation.hpp"
#include "Net.hpp"
#include "Server.hpp"
#include "WorldManager.hpp"
#include "BundleInfo.hpp"

#include "LuaEngine.hpp"

namespace spelprog2 {

struct UnhandledMessageHandler : public net::NetMessageListener {
    virtual void onMessage(MessagePtr msg, const net::NetMessageInfo & info)
    {
        throw Exception(0, "BUG: Unhandled network message of type " + msg->GetTypeName() +
            " from user #" + STRING_CAST(info.origin), WHERE);
    }
};

Game::Game(const Ogre::String & ogrePluginFile) :
		lengine(),
        root_(new Ogre::Root(ogrePluginFile)),
        presentation_(),
        queuedPresentation_(),
        frameHandler_(),
        window_(),
        view_(),
        camera_(),
        topDownCamera_(),
		soundEngine_(),
        net_(),
        worldMan_(),
        netInitMode_(LOCAL),
        host_(""),
        port_(0),
        renderTarget_(0),
        username_(""),
        password_("")
{
    //let user choose config
    gamelog << "Asking user for config";
	root_->restoreConfig();
	if (!root_->showConfigDialog()) {
        exit(0);
	}

    gamelog << "Initing window";
	window_ = root_->initialise(true, "Spelprog2: T3h Project");
}

Game::~Game()
{
	if (soundEngine_) {
		soundEngine_->drop();
	}

    delete frameHandler_;
    delete presentation_;
    delete worldMan_;
    delete net_;

    //ok, this looks kind of retarded, but I guess that's the choice for QuickGUI design :)
    //eiter way, it does get rid of everything GUI related so it's not necessary to destroy anything manual (except the above)
    delete QuickGUI::Root::getSingletonPtr();

    delete root_;

	delete lengine;
}

void Game::initCamera()
{
    camera_ = presentation_->getSceneManager().createCamera("Camera");
    view_ = window_->addViewport(camera_);
    camera_->setAspectRatio(Ogre::Real(view_->getActualWidth()) / view_->getActualHeight());
    camera_->setNearClipDistance(Ogre::Real(0.1));

    const bool canHaveInfiniteFarPlane = root_->getRenderSystem()->getCapabilities()
        ->hasCapability(Ogre::RSC_INFINITE_FAR_PLANE);
    camera_->setFarClipDistance(Ogre::Real(canHaveInfiniteFarPlane? 0 : 10000));
}


void Game::initTopDownCamera()
{
    topDownCamera_ = presentation_->getSceneManager().createCamera("TopDownCamera");
    topDownCamera_->setAutoAspectRatio(true);
    topDownCamera_->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
    topDownCamera_->setPosition(Ogre::Vector3(500.0f, 1000.0f, 500.0f));
    Ogre::Radian pitch(-FLOAT_PI * 0.5f);
    topDownCamera_->pitch(pitch);
}
void Game::updateTopDownCamera()
{
}

Ogre::RenderTarget *Game::setupRTTTarget(const Ogre::String & name, int width, int height){
    using namespace Ogre;
    TexturePtr tex = TextureManager::getSingleton().createManual(name,
				ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, width, height, 0, PF_R8G8B8, TU_RENDERTARGET);

    return tex->getBuffer()->getRenderTarget();
}

void Game::initNet()
{
    delete net_;

    switch (netInitMode_) {
        case LOCAL: {
            gamelog << "Trying to init local network service emulation";
            net::netlog.rebind("Local.log");
            net_ = new net::Local();
            break;
        }
        case SERVER: {
            gamelog << "Trying to init server network service";
            net::netlog.rebind("Server.log");
            net_ = new net::Server(port_, password_, true, username_);
            break;
        }
        case CLIENT: {
            gamelog << "Trying to init client network service";
            net::netlog.rebind("Client.log");
            net_ = new net::Client(host_, port_, password_, username_);
            break;
        }
    }

    //net_->addUnhandledMsgListener(net::NetMessageListener::Pointer(new UnhandledMessageHandler()));
    presentation_->registerNetListeners();
    worldMan_->registerNetListeners();
}

void Game::run()
{
    QuickGUI::registerScriptReader();
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

    gamelog << "Initializing QuickGUI root";
    new QuickGUI::Root(); //this call looks absurd, but it initializes a singleton
    QuickGUI::SkinTypeManager::getSingleton().loadTypes();

    gamelog << "Initing world manager";
    worldMan_ = new sim::WorldManager(*this);

    gamelog << "Presenting start menu";
    queuePresentation(new presentation::MenuPresentation(*this, "hell"));

    initNet();

    gamelog << "Initializing frame handler";
    frameHandler_ = new FrameHandler(*this);
    root_->addFrameListener(frameHandler_);


	//TEST//////////////////////////////////////////////////////////////////
	// start irrKlang with default parameters
	soundEngine_ = irrklang::createIrrKlangDevice();
	printf("\n\n");
	printf((getBundlePath(PLUGIN_DEVIATION) + "irrklang").c_str());
	printf("\n\n");
	if(soundEngine_->loadPlugins((getBundlePath(PLUGIN_DEVIATION) + "irrklang").c_str()))
	    printf("###Custom IrrKlang plugin loading successful\n");
	else printf("###Custom IrrKlang plugin loading FAILED\n");;

	if (!soundEngine_) {
		gamelog << "Failed to init irrKlang!";
		return; // error starting up the engine
	}
	else {
		// play some sound stream, looped
		soundEngine_->play2D((getBundlePath(RESOURCE_DEVIATION) + "media/music/test.mp3").c_str(), true);
		//!TEST/////////////////////////////////////////////////////////////////
	}


	lengine = new lua::Engine();
	lengine->dofile("gamescript/main.lua");

	
    gamelog << "Initialization finished, starting main loop";
	root_->startRendering();
}

void Game::locateResources(const Ogre::String & filename)
{
    if(!renderTarget_){
        //renderTarget_ = setupRTTTarget("unit_shadows");
    }

    //Code adapted from http://artis.imag.fr/~Xavier.Decoret/resources/ogre/tutorial1.html
    Ogre::ConfigFile cf;
    cf.load(filename);   
    Ogre::String bundlePath = getBundlePath(RESOURCE_DEVIATION);

    Ogre::ConfigFile::SectionIterator it = cf.getSectionIterator();   
    while (it.hasMoreElements()) {
      Ogre::String secName = it.peekNextKey();
      Ogre::ConfigFile::SettingsMultiMap* settings = it.getNext();
      for (Ogre::ConfigFile::SettingsMultiMap::iterator i = settings->begin();
                i != settings->end(); ++i) {
            const Ogre::String typeName = i->first;
            const Ogre::String archName = i->second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(bundlePath + archName, typeName, secName);
        }
    }
}

void Game::setAsClient(const std::string & username, const std::string & password, const std::string & host, unsigned port)
{
    netInitMode_ = CLIENT;
    host_ = host;
    port_ = port;
    username_ = username;
    password_ = password;
}

void Game::setAsServer(const std::string & username, const std::string & password, unsigned port)
{
    netInitMode_ = SERVER;
    port_ = port;
    username_ = username;
    password_ = password;
}

void Game::setAsLocal()
{
    netInitMode_ = LOCAL;
}

void Game::quit()
{
    frameHandler_->setQuitFlag();
}

void Game::queuePresentation(presentation::Presentation * newPresentation)
{
    assert(!queuedPresentation_);
    queuedPresentation_ = newPresentation;

    if (!presentation_) {
        present();
    }
}

void Game::present()
{
    //the reason for this method being so complicated is the overlapping life time requirements of various objects

    if (!queuedPresentation_) {
        return;
    }

    window_->removeAllViewports();

    if (presentation_) {
        gamelog << "Removing old presentation";
        if (net_) {
            presentation_->unregisterNetListeners();
        }
        
        presentation_->getSceneManager().destroyCamera("TopDownCamera");
        presentation_->getSceneManager().destroyCamera("Camera");

        delete presentation_;
    }

    presentation_ = queuedPresentation_;

    gamelog << "Setting up presentation scene";
    presentation_->initScene();

    gamelog << "Initializing camera and viewport";
    initCamera();
    presentation_->initCameraManager();
    
    if(renderTarget_) {
		initTopDownCamera();
        renderTarget_->removeAllViewports();
        Ogre::Viewport *view = renderTarget_->addViewport(topDownCamera_);
        view->setOverlaysEnabled(false);
        view->_updateDimensions();
        view->setMaterialScheme("unit_shadows");
    }
    
    gamelog << "Initing GUI for presentation";
    presentation_->initGUI();

    if (net_) {
        presentation_->registerNetListeners();
    }

    queuedPresentation_ = 0;
}

};
